/// Text Overlay Application: Structures Class
/// This class contains all structures used in the application.

using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using Microsoft.DirectX.Direct3D;

namespace TextOverlay
{
    /// <summary>
    /// Blocks are units in a timeline, they are composed by the Index of the combo to
    /// render, its start frame and its end frame.
    /// </summary>
    struct Block
    {
        public int ComboIndex;
        public int startFrame;
        public int endFrame;
    }

    /// <summary>
    /// ComboSchedules are collections of blocks (timelines)
    /// </summary>
    struct ComboSchedule
    {
        public Block[] SchCombo;
        public ComboSchedule(int size)
        {
            SchCombo = new Block[size];
        }
    }

    /// <summary>
    /// The structure DXFont contains the necessary information to create a DirectX font
    /// </summary>
    struct DXFont
    {
        public int Size;
        public string Face;
        public bool Bold;
        public bool Italics;

        public DXFont(string face, int size, bool bold, bool italics)
        {
            this.Face = face;
            this.Size = size;
            this.Bold = bold;
            this.Italics = italics;
        }
    }

    /// <summary>
    /// StringEx is a complex string, used by the RebuildString() function to store a text string
    /// with a determinated position, font id, font color and background color.
    /// </summary>
    public struct StringEx
    {
        public System.Drawing.Point Position;
        public System.Drawing.Color Color;
        public string String ;
        public int FontID;
        public System.Drawing.Color bgcolor;

        public StringEx(StringEx Clone)
        {
            this.Position = Clone.Position;
            this.Color = Clone.Color;
            this.String = Clone.String;
            this.FontID = Clone.FontID;
            this.bgcolor = Clone.bgcolor;
        }

        public void Clear()
        {
            this.Position.X = 0;
            this.Position.Y = 0;
            this.Color = System.Drawing.Color.White;
            this.FontID = 0;
            this.bgcolor = System.Drawing.Color.Black;
        }

        public bool Compare(Element fragment)
        {
            if (fragment.hierarchy == 0)
                return false;
            if (this.Color.ToArgb() != fragment.FontColor.ToArgb())
                return false;
            if (this.FontID != fragment.FontID)
                return false;
            if (this.bgcolor.ToArgb() != fragment.BgColor.ToArgb())
                return false;
            return true;
        }
    }

    /// <summary>
    /// This structure contains the necessary information to create a usable DirectX image
    /// </summary>
    public class DXImage
    {
        public Microsoft.DirectX.Direct3D.Texture Image;
        public Microsoft.DirectX.Direct3D.Sprite XSprite;
        public string Name;
        public int ID;
        public string Update;
        public string Path;
        public float Width;
        public float Height;

        public DXImage()
        {

        }
    }


    /// <summary>
    /// This structure represents a font used to measure strings. The structure 
    /// contains 200 premeasured characters.
    /// </summary>
    public struct MeasuringFont
    {
        public int[] CharWidth;
        public string Description;

        public MeasuringFont(int size)
        {
            CharWidth = new int[200];
            Description = "";
        }
    }


    public struct MonitorPoint
    {
        public int Span;
        public string Name;

        public MonitorPoint(string name)
        {
            Span = 0;
            Name = name;
        }

        public void Report(int limit)
        {
            if (Span > limit)
                Track.LogInfo("Monitor Point \"" + Name + "\" took " + Span.ToString() + " milliseconds");
        }
    }

    /// <summary>
    /// This structure represents the Events Log of the application.
    /// </summary>
    public struct LogBuffer
    {
        public string[] Entry;
        private int Index;
        private int i;
        private int top;
        private static System.Object LogLock = new System.Object();

        /// <summary>
        /// Creates a Log buffer
        /// </summary>
        /// <param name="size">number of entries to keep in the log</param>
        public LogBuffer(int size)
        {
            Entry = new string[size];
            for (i = 0; i < size; i++)
                Entry[i] = "";
            Index = 0;
            i = 0;
            top = 0;
        }

        /// <summary>
        /// Appends a new entry to the log
        /// </summary>
        /// <param name="message"></param>
        public void AppendToLog(string message)
        {
            lock (LogLock)
            {
                Entry[Index] = DateTime.Now.ToLongTimeString() + " -> " + message;
                Index++;
                if (Index >= Entry.Length)
                    Index = 0;
            }
        }

        /// <summary>
        /// Renders the log on the screen
        /// </summary>
        /// <param name="sprite">Direct3D sprite</param>
        /// <param name="DxFont">Direct3D font</param>
        /// <param name="x">x position</param>
        /// <param name="y">y position</param>
        public void RenderLog(Sprite sprite, Font DxFont, int x, int y)
        {
            top = y;
            
            for (i = Index; i < Entry.Length; i++)
            {
                DxFont.DrawText(sprite, Entry[i], x, top, System.Drawing.Color.White);
                top += 15;
            }

            for (i = 0; i < Index; i++)
            {
                DxFont.DrawText(sprite, Entry[i], x, top, System.Drawing.Color.White);
                top += 15;
            }
        }  
    }

    /// <summary>
    /// CDisplay is a portion of black texture used a rendering target (i.e. Ribbons, Mitsu, etc)
    /// this structure contains the texture, surface, sprite and all its size and positioning information.
    /// </summary>
    public struct CDisplay
    {
        public int Channel;
        public int Override;
        public Texture m_Texture;
        public Surface m_Surface;
        public Sprite m_Sprite;
        public int OffsetX;
        public int OffsetY;
        public int OutPosX;
        public int OutPosY;
        public int Width;
        public int Height;

        public float cover_x;
        public float cover_y;
        public float cover_w;
        public float cover_h;

        public bool clear;

        public CDisplay(CDisplay Clone)
        {
            this.Channel = Clone.Channel;
            this.Override = Clone.Override;
            this.m_Texture = Clone.m_Texture;
            this.m_Surface = Clone.m_Surface;
            this.m_Sprite = Clone.m_Sprite;
            this.OffsetX = Clone.OffsetX;
            this.OffsetY = Clone.OffsetY;
            this.OutPosX = Clone.OutPosX;
            this.OutPosY = Clone.OutPosY;
            this.Override = Clone.Override;
            this.Width = Clone.Width;
            this.Height = Clone.Height;
            this.cover_h = Clone.cover_h;
            this.cover_w = Clone.cover_w;
            this.cover_x = Clone.cover_x;
            this.cover_y = Clone.cover_y;
            this.clear = Clone.clear;
        }

        /// <summary>
        /// Tests if a given point is located inside this surface using only its Y value
        /// </summary>
        /// <param name="y">Y value of the point to test</param>
        /// <returns></returns>
        public bool ContainsPoint(int y)
        {
            if (y < (this.OffsetY + this.Height) && y > this.OffsetY)
                return true;
            else
                return false;
        }
    }

    /// <summary>
    /// This texture contains a List Collection of combos to be used for timelines
    /// </summary>
    struct ComboLib
    {
        public delegate int CallbackFromMain(string Facename, int TheSize);
        public delegate int CallbackMeasure(string mystring, string facename, int size);
        public delegate int CallbackImageSize(int id);
        private CallbackFromMain FontIDCallBack;
        private CallbackMeasure MeasureStringCallBack;
        private CallbackImageSize MeasureImageCallBack;
        public List<Combo> Collection;
        private CExpressions Expression;

        public ComboLib(CallbackFromMain aCallback, CallbackMeasure MeasureCallBack, CallbackImageSize MeasureImage)
        {
            FontIDCallBack = aCallback;
            MeasureStringCallBack = MeasureCallBack;
            MeasureImageCallBack = MeasureImage;
            Expression = new CExpressions(true);
            Collection = new List<Combo>();
            Expression = new CExpressions();
        }
    }

    /// <summary>
    /// This structure contains all the information of a Text Combo
    /// </summary>
    struct Combo
    {
        public int HashID;
        public int StringLength;
        public string FullXml;
        public string ComboID;
        public string ComboName;
        public string StartTime;
        public string Duration;
        public int StartFrame;
        public int EndFrame;
        public Element[] Fragment;
        public int ScaleFactor;

        public Combo(string fullnode, int fragments, int Scale, string id, string name, string startTime, string duration)
        {
            StringLength = 0;
            ScaleFactor = Scale;
            ComboID = id;
            ComboName = name;
            StartTime = startTime;
            Duration = duration;
            Fragment = new Element[fragments];
            FullXml = fullnode;
            HashID = 0;
            StartFrame = 0;
            EndFrame = 0;
        }


        public Combo(Combo Clone)
        {
            this.HashID = Clone.HashID;
            this.StringLength = Clone.StringLength;
            this.ComboID = Clone.ComboID;
            this.ComboName = Clone.ComboName;
            this.StartTime = Clone.StartTime;
            this.Duration = Clone.Duration;
            this.StartFrame = Clone.StartFrame;
            this.EndFrame = Clone.EndFrame;
            this.ScaleFactor = Clone.ScaleFactor;
            this.Fragment = new Element[Clone.Fragment.Length];
            this.FullXml = Clone.FullXml;
            for (int e = 0; e < Clone.Fragment.Length; e++)
                this.Fragment[e] = Clone.Fragment[e];
        }

        /// <summary>
        /// This function is called by the ParseCombo() function in the CTimeline class to 
        /// create the Array of elements (fragments of the main text string) for each combo.
        /// </summary>
        /// <param name="nofElements">the number of elements contained in the combo</param>
        public void CreateElementsArray(int nofElements)
        {
            Fragment = new Element[nofElements];
        }

        /// <summary>
        /// This function calculates the duration in frames of the combo
        /// </summary>
        public void CalculateFrames()
        {
            StartFrame = (Convert.ToInt32(StartTime.Substring(0, 2)) * 216000) + (Convert.ToInt32(StartTime.Substring(3, 2)) * 3600) + (Convert.ToInt32(StartTime.Substring(6, 2)) * 60) + Convert.ToInt32(StartTime.Substring(9));
            int DurationFrame = (Convert.ToInt32(Duration.Substring(0, 2)) * 216000) + (Convert.ToInt32(Duration.Substring(3, 2)) * 3600) + (Convert.ToInt32(Duration.Substring(6, 2)) * 60) + Convert.ToInt32(Duration.Substring(9));
            EndFrame = StartFrame + DurationFrame;
        }

        /// <summary>
        /// This function is called by the CreateCombo function in CTimeline class 
        /// The objetive of this function is to parse each element of the target combo 
        /// extracting all its necessary values from its detail string.
        /// </summary>
        /// <param name="index">the index of the element (fragment) in the array of elements of the combo</param>
        /// <param name="inc_string">the fragment's text string</param>
        /// <param name="inc_details">the fragment's details string</param>
        /// <param name="Expression">pointer to an instance of the Expression's class</param>
        public void ParseElement(int index, string inc_string, string inc_details, CExpressions Expression)
        {
            // Set Default Values
            Fragment[index].FontColor = System.Drawing.Color.White;

            // Get Start and End Frames
            Fragment[index].startframe = Convert.ToInt32(Expression.GetDataTag("STARTFRAME=\"", inc_details));
            Fragment[index].endframe = Convert.ToInt32(Expression.GetDataTag("ENDFRAME=\"", inc_details));
            Fragment[index].duration = Fragment[index].endframe - Fragment[index].startframe;

            if (Expression.GetDataTag("STARTX=\"", inc_details) != "")
                Fragment[index].startx = Convert.ToInt32(Expression.answer) * ScaleFactor;
            if (Expression.GetDataTag("ENDX=\"", inc_details) != "")
                Fragment[index].endx = Convert.ToInt32(Expression.answer) * ScaleFactor;
            if (Expression.GetDataTag("STARTY=\"", inc_details) != "")
                Fragment[index].starty = Convert.ToInt32(Expression.answer) * ScaleFactor;
            if (Expression.GetDataTag("ENDY=\"", inc_details) != "")
                Fragment[index].endy = Convert.ToInt32(Expression.answer) * ScaleFactor;

            Fragment[index].ConstantSpeedX = (Fragment[index].endx - Fragment[index].startx) / Fragment[index].duration;

            if (Expression.GetDataTag("FONTSIZE=\"", inc_details) != "")
                Fragment[index].FontSize = Convert.ToInt32(Expression.answer) * ScaleFactor;

            Fragment[index].FontFace = Expression.GetFontFace(inc_details);
            Fragment[index].FontColor = Expression.ReadFontColor(inc_details);

            Fragment[index].BgColor = Expression.ReadBackColor(inc_details);
            Fragment[index].BgImage = Expression.ReadBackImage(inc_details);
            Fragment[index].RevealIn = Expression.ReadRevealIn(inc_details);
            if (Fragment[index].RevealIn.Length > 0)
            {
                Fragment[index].RevealX = Expression.GetIntValue(Fragment[index].RevealIn, 0) * ScaleFactor;
                Fragment[index].RevealY = Expression.GetIntValue(Fragment[index].RevealIn, 1) * ScaleFactor;
                Fragment[index].RevealW = Expression.GetIntValue(Fragment[index].RevealIn, 2) * ScaleFactor;
                Fragment[index].RevealH = Expression.GetIntValue(Fragment[index].RevealIn, 3) * ScaleFactor;
                Fragment[index].RevealF = Expression.GetIntValue(Fragment[index].RevealIn, 4);
            }


            Expression.GetDataTag("TARGETSPEED=\"", inc_details);
            if (Expression.answer.Length > 0)
                Fragment[index].TargetSpeed = Expression.GetIntValue(Expression.answer);

            Expression.GetDataTag("MATCHSPEEDENDPOSITION=\"", inc_details);
            if (Expression.answer.Length > 0)
                Fragment[index].TargetPixel = Expression.GetIntValue(Expression.answer) * ScaleFactor;
            else
                Fragment[index].TargetPixel = 0;


            Expression.GetDataTag("ACCELERATEIN=\"", inc_details);
            if (Expression.answer.Length > 0)
                Fragment[index].AccelerateIn = Expression.answer;

            Expression.GetDataTag("ACCELERATEOUT=\"", inc_details);
            if (Expression.answer.Length > 0)
                Fragment[index].AccelerateOut = Expression.answer;

            Expression.GetDataTag("SWEEPIN=\"", inc_details);
            if (Expression.answer.Length > 0)
                Fragment[index].SweepIn = Expression.answer;

            Expression.GetDataTag("SWEEPOUT=\"", inc_details);
            if (Expression.answer.Length > 0)
                Fragment[index].SweepOut = Expression.answer;

            if (Expression.GetDataTag("CONSTANTSPEED=\"", inc_details) != "")
            {
                Fragment[index].ConstantSpeedX = Expression.GetIntValue(Expression.answer) * ScaleFactor;
                Fragment[index].ConstantSpeedY = Expression.GetIntValue(Expression.answer, 1) * ScaleFactor;
            }

            Fragment[index].m_details = inc_details;
            Fragment[index].m_string = inc_string.Replace("_", " ");

        }

    }

    /// <summary>
    /// This structure contains all the information of an Element
    /// </summary>
    public struct Element
    {
        // String Details
        public string m_string;
        public string m_details;
        public int stringwidth;
        public System.Drawing.Point lastPosition;

        // Font Details
        public int FontID;                    // Font id from our font collection
        public string FontFace;
        public int FontSize;
        public System.Drawing.Color FontColor;
        public int hierarchy;
        public int distance;

        public int startx;                      // Element Settings
        public int starty;                      //  ""   ""
        public int endx;                        //  ""   ""
        public int endy;                        //  ""   ""
        public int startframe;                  //  ""   ""
        public int endframe;                    //  ""   ""

        public float ConstantSpeedX;        //  ""   ""        
        public int ConstantSpeedY;

        public string RevealIn;
        public int RevealX;
        public int RevealY;
        public int RevealW;
        public float RevealH;
        public float RevealF;

        public string SweepIn;                     //  ""   ""
        public string SweepOut;                    //  ""   ""
        public string AccelerateIn;
        public string AccelerateOut;
        public int FallIn;                      //  ""   ""
        public string Hold;                    //  ""   ""
        public int HoldingPoint;                //  ""   ""
        public ArrayList HoldingFrame;
        public int FramesHolded;
        public bool isHolding;
        public float TargetSpeed;                 //  ""   ""
        public int TargetPixel;
        public int duration;

        public System.Drawing.Color BgColor;
        public int BgImage;
        public bool Visible;

        public Element(Element Clone)
        {
            this.m_string = Clone.m_string;
            this.m_details = Clone.m_details;
            this.lastPosition = Clone.lastPosition;
            this.stringwidth = Clone.stringwidth;
            this.distance = Clone.distance;
            this.FontID = Clone.FontID;
            this.FontFace = Clone.FontFace;
            this.FontSize = Clone.FontSize;
            this.FontColor = Clone.FontColor;
            this.hierarchy = Clone.hierarchy;
            this.startx = Clone.startx;
            this.starty = Clone.starty;
            this.endx = Clone.endx;
            this.endy = Clone.endy;
            this.startframe = Clone.startframe;
            this.endframe = Clone.endframe;
            this.duration = Clone.duration;
            this.AccelerateIn = Clone.AccelerateIn;

            this.AccelerateOut = Clone.AccelerateOut;
            this.ConstantSpeedX = Clone.ConstantSpeedX;
            this.ConstantSpeedY = Clone.ConstantSpeedY;

            this.SweepIn = Clone.SweepIn;
            this.SweepOut = Clone.SweepOut;
            this.FallIn = Clone.FallIn;
            this.Hold = Clone.Hold;
            this.HoldingPoint = Clone.HoldingPoint;
            this.TargetSpeed = Clone.TargetSpeed;
            this.TargetPixel = Clone.TargetPixel;
            this.FramesHolded = Clone.FramesHolded;
            this.isHolding = Clone.isHolding;
            this.BgColor = Clone.BgColor;
            this.BgImage = Clone.BgImage;

            this.RevealIn = Clone.RevealIn;
            this.Visible = Clone.Visible;
            this.RevealF = Clone.RevealF;
            this.RevealH = Clone.RevealH;
            this.RevealW = Clone.RevealW;
            this.RevealX = Clone.RevealX;
            this.RevealY = Clone.RevealY;

            this.HoldingFrame = new ArrayList();

        }
    }
}
